/**
 * \file map.cpp
 * \brief Map class header
 * \author Marc Laval
 * \version 0.1
 * \date 10/2011
 *
 */

#include "carte.h"

//--------------------------------------------------------------------------------------------------------
/*! \brief Default constructor */
Map::Map()
{
    m_width = 0;
    m_height = 0;
    m_tileSize = 0;
	m_cells = NULL;
}

//--------------------------------------------------------------------------------------------------------
/*! \brief Constructor
 *  \param width Map width
 *  \param height Map height
 *  \param tileSize Tile size
 */
Map::Map(uint16 with, uint16 height, uint8 tileSize)
{
    m_width = width;
    m_height = height;
    m_tileSize = tileSize;
	m_cells = new Cell**[m_width];

	for (int i=0;i<m_width;i++)
	{
		m_cells[i] = new Cell*[m_height];

		for (int j=0;j<m_height;j++)
		{
			grille[i][j] = new Cell();
		}
	}
}

//--------------------------------------------------------------------------------------------------------
/*! \brief Destructor */
Map::~Map()
{
    if(m_cells != NULL)
    {
        for(int i=0;i<m_width;i++)
        {
            for(int j=0;j<m_height;j++)
            {
                delete(m_cells[i][j]);
            }

            delete [] (m_cells[i]);
        }
        delete(m_cells);
    }
}

//--------------------------------------------------------------------------------------------------------
/*! \brief Load a map form an existing file
 *  \param filename file name
 *  \return true if success, false otherwise. */
bool Map::loadFromFile(std::string filename)
{
    int i,j,k;

    //Try to open the file
	std::ifstream inputfile(filename.c_str(), std::ios_base::binary);

    //Opening failed
    if(inputfile.fail())
	{
	    LogFile::getInstance()->log(LogFile::ltError, "In Map::loadFromFile : cannot open file "+filename);
		return false;
	}
	else
	{
	    //To get the file size
        //We set the cursor to the end of file
        inputfile.seekg(0, std::ios_base::end);
        //We get the size
        uint32 fileSize = fileToSend.tellg();
        //Set the cursor at file start
        inputfile.seekg(0, std::ios_base::beg);

        ByteBuffer byteBuff;
        byte buffer[FILE_BLOCK_SIZE];
        uint32 octToRead = fileSize;

		// File read
		while(octToRead >= FILE_BLOCK_SIZE)
		{
            inputfile.read((char*)buffer, FILE_BLOCK_SIZE);
            //Read data are copied into the buffer
            byteBuff.insert(byteBuff.end(), buffer, buffer+FILE_BLOCK_SIZE);
            octToRead -= FILE_BLOCK_SIZE;
		}

		 inputfile.read((char*)buffer, octToRead);
         //Read data are copied into the buffer
         byteBuff.insert(byteBuff.end(), buffer, buffer+octToRead);

        //Header
        Deserializer des(&byteBuff);
        char Mcar = des.readUInt8();
        char Acar = des.readUInt8();
        char Pcar = des.readUInt8();

        // Bad file Header
        if(Mcar != 'M' || Acar != 'A' || Pcar != 'P')
        {
            throw(Error e("Error : incorrect file type");
        }

        //Extended format is not managed yet
        //The byte is read, but not used
        des.readUInt8();

        m_width = des.readUInt16();
        m_height = des.readUInt16();
        m_tileSize = des.readUInt8();

        uint32 expectedFileSize = 9 + (3 * m_width * m_height);

        //Compare file size
        if(fileSize != expectedFileSize)
        {

            throw(Error e("Error : incorrect file size");
        }

		//Creation de la grille de pointeurs
		this->m_cells = new Cell**[m_height];
		CellType ct;
		uint16 tileGround;

		for (i=0;i<m_height;i++)
		{
			this->m_cells[i] = new Cell*[m_width];
			for(j=0;j<m_width;j++)
			{
				this->m_cells[i][j] = Cell::deserilize(&des);
			}
		}

		return true;
	}

}

//--------------------------------------------------------------------------------------------------------
/*! \brief Save a map to a file
 *  \param filename file name
 *  \return true if success, false otherwise. */
bool Map::saveToFile(std::string filename)
{
    int i,j,k;

    //Try to open the file
    std::ofstream ouputfile(filename.c_str(), ios::out | ios::trunc |std::ios_base::binary);

    //Opening failed
    if(outputfile.fail())
	{
	    LogFile::getInstance()->log(LogFile::ltError, "In Map::saveToFile : cannot open file "+filename);
		throw(Error e("Error : cannot open file "+filename));
	}
	else
	{
        ByteBuffer byteBuff;

        //Header
        Serializer ser(&byteBuff);
        ser.serialize((uint8)'M');
        ser.serialize((uint8)'A');
        ser.serialize((uint8)'P');

        //Extended format is not managed yet
        //The byte is read, but not used
        ser.serialize((uint8) 0);

        ser.serialize(m_width);
        ser.serialize(m_height);
        ser.serialize(m_tileSize);

		//Saving the cells
		uint16 tileGround;

		for (i=0;i<m_height;i++)
		{
			for(j=0;j<m_width;j++)
			{
			    m_cells[i][j]->serialize(&ser);
			}
		}

		//Save the serilized data into the file
        outputfile.write((char*)&byteBuff[0], bytebuff.size());

        uint32 octToWrite = bytebuff.size()
		// File read
		while(octToRead >= FILE_BLOCK_SIZE)
		{
            inputfile.read((char*)buffer, FILE_BLOCK_SIZE);
            //Read data are copied into the buffer
            byteBuff.insert(byteBuff.end(), buffer, buffer+FILE_BLOCK_SIZE);
            octToRead -= FILE_BLOCK_SIZE;
		}

        return true;
	}
}

//--------------------------------------------------------------------------------------------------------
/*! \brief Getter of the map width
 *  \return Map width in tiles
 */
uint16 Map::getWidth()
{
    return this->m->width;
}

//--------------------------------------------------------------------------------------------------------
/*! \brief Getter of map height
 *  \return Map height in tiles
 */
uint16 Map::getHeight()
{
    return this->m_height;
}

//--------------------------------------------------------------------------------------------------------
/*! \brief Getter of tile size. (Tiles are always square shaped so both sides have same size)
 *  \return Tile size in pixels
 */
uint8 Map::getTileSize()
{
    return this->m_tileSize;
}

//--------------------------------------------------------------------------------------------------------
/*! \brief Try to get a cell from the map
 *  \param pos Position of the cell in the whole map.
 *  \return Pointer to the cell if coordinates are correct, null (0) otherwise.
 *  \throw Error report if coordinates are incorrect
 */
Cell* Map::getCell(Point& pos) throw (Error)
{
    if(pos.getX() >= this->m_width || pos.getY() >= this->m_height)
	{

		LogFile::getInstance()->log(LogFile::ltError, " Map::getCell : Index out of bounds");
		return 0;
	}
	else
		return this->m_cells[pos.getX()][pos.getY()];
}

//--------------------------------------------------------------------------------------------------------
/*! \brief Paint the map on the screen.
 *  \param screen SDL surface to paint on the screen
 *  \param r Map area to paint on the screen. null for whole map.
 *
 * When the map is painted on the screen, it is possible to
 * only draw a limited part of the map defined by 'r'.
 * The paint method automatically computes positions to ensure
 * that coordinates (and so part of the rect) are not outside of the map.
 */
void Map::paint(SDL_Surface * screen, SDL_Rect * r)
{
    //TODO Implementation Map::Paint
}


